﻿using System;
using System.IO;
using System.Linq;
using Hallenberg.Core.IO;
using NUnit.Framework;

namespace Hallenberg.Core.Test.IO
{
    [TestFixture]
    public class CSVFileTest
    {
        [Test]
        public void Core_IO_CSVFile_Load_PassNull_Expect_Fail()
        {
            CSVFile csvFile = new CSVFile();

            ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => csvFile.Load(null));

            Assert.That(exception, Is.Not.Null);
            Assert.That(exception.ParamName, Is.EqualTo("filePath"));
        }

        [Test]
        public void Core_IO_CSVFile_ReadUnknownFile_Fail()
        {
            CSVFile csvFile = new CSVFile();

            FileNotFoundException exception = Assert.Throws<FileNotFoundException>(() => csvFile.Load("no_such_file"));

            Assert.That(exception, Is.Not.Null);
        }

        [Test]
        public void Core_IO_CSVFile_ReadFile_No_Header()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithoutHeader.csv");

            Assert.That(csvFile.Rows.Count, Is.EqualTo(3));
            Assert.That(csvFile.Rows[0][0], Is.EqualTo("First"));
            Assert.That(csvFile.Rows[0][1], Is.EqualTo("Second"));
            Assert.That(csvFile.Rows[0][2], Is.EqualTo("Third"));
            Assert.That(csvFile.Rows[1][0], Is.EqualTo("Fourth"));
            Assert.That(csvFile.Rows[1][1], Is.EqualTo("Fifth"));
            Assert.That(csvFile.Rows[1][2], Is.EqualTo("Sixth"));
            Assert.That(csvFile.Rows[2][0], Is.EqualTo("Seventh"));
            Assert.That(csvFile.Rows[2][1], Is.EqualTo("Eighth"));
            Assert.That(csvFile.Rows[2][2], Is.EqualTo("Ninth"));
        }

        [Test]
        public void Core_IO_CSVFile_ReadFile_Uneven_No_Header()
        {
            CSVFile csvFile = new CSVFile();

            InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() =>
                csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithoutHeaderUneven.csv"));

            Assert.That(exception, Is.Not.Null);
            Assert.That(exception.Message, Is.EqualTo("Rows contains different number of columns"));
        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            Assert.That(csvFile.Header[0], Is.EqualTo("Column A"));
            Assert.That(csvFile.Header[1], Is.EqualTo("Column B"));
            Assert.That(csvFile.Header[2], Is.EqualTo("Column C"));
            Assert.That(csvFile.Rows[0]["Column A"], Is.EqualTo("Value One"));
            Assert.That(csvFile.Rows[0]["Column B"], Is.EqualTo("Value Two"));
            Assert.That(csvFile.Rows[0]["Column C"], Is.EqualTo("Value Three"));
            Assert.That(csvFile.Rows[1]["Column A"], Is.EqualTo("Value Four"));
            Assert.That(csvFile.Rows[1]["Column B"], Is.EqualTo("Value Five"));
            Assert.That(csvFile.Rows[1]["Column C"], Is.EqualTo("Value Six"));
        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header_Access_Wrong_Column()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            string columnValue;
            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(() => columnValue = csvFile.Rows[0]["Column D"]);

            Assert.That(exception, Is.Not.Null);
            Assert.That(exception.ParamName, Is.EqualTo("index"));
        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header_Break_Row_Upper_Bounds()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            string columnValue;
            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(() => columnValue = csvFile.Rows[10][0]);

            Assert.That(exception, Is.Not.Null);
        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header_Break_Row_Lower_Bounds()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            string columnValue;
            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(() => columnValue = csvFile.Rows[-1][0]);

            Assert.That(exception, Is.Not.Null);

        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header_Break_Column_Lower_Bounds()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            string columnValue;
            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(() => columnValue = csvFile.Rows[0][-1]);

            Assert.That(exception, Is.Not.Null);
        }

        [Test]
        public void Core_IO_CSVFile_Read_With_Header_Break_Column_Upper_Bounds()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithHeader.csv", true);

            string columnValue;
            ArgumentOutOfRangeException exception = Assert.Throws<ArgumentOutOfRangeException>(() => columnValue = csvFile.Rows[0][10]);

            Assert.That(exception, Is.Not.Null);
        }

        [Test]
        public void Core_IO_CSVFile_Read_Empty_File_Expect_Fail()
        {
            CSVFile csvFile = new CSVFile();
            InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() => csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\EmptyFile.csv", true));

            Assert.That(exception, Is.Not.Null);
            Assert.That(exception.Message, Is.EqualTo("File is empty"));
        }

        [Test]
        public void Core_IO_CSVFile_GetSeparators()
        {
            CSVFile csvFile = new CSVFile();

            char[] separators = csvFile.GetSeparators();

            Assert.That(separators.Count(), Is.EqualTo(2));
            Assert.That(separators[0], Is.EqualTo(','));
            Assert.That(separators[1], Is.EqualTo(';'));
        }

        [Test]
        public void Core_IO_CSVFile_SetSeparators_Pass_Null_Expect_Fail()
        {
            CSVFile csvFile = new CSVFile();
            
            ArgumentNullException exception = Assert.Throws<ArgumentNullException>(() => csvFile.SetSeparators(null));

            Assert.That(exception, Is.Not.Null);
        }

        [Test]
        public void Core_IO_CSVFile_ReadFile_No_Header_With_Custom_Separator()
        {
            CSVFile csvFile = new CSVFile();
            csvFile.SetSeparators(new char[] { '!' });
            csvFile.Load(@"..\..\..\Hallenberg.Core.Test\IO\CSVFileWithoutHeaderCustomSeparator.csv");

            Assert.That(csvFile.Rows.Count, Is.EqualTo(3));
            Assert.That(csvFile.Rows[0][0], Is.EqualTo("First"));
            Assert.That(csvFile.Rows[0][1], Is.EqualTo("Second"));
            Assert.That(csvFile.Rows[0][2], Is.EqualTo("Third"));
            Assert.That(csvFile.Rows[1][0], Is.EqualTo("Fourth"));
            Assert.That(csvFile.Rows[1][1], Is.EqualTo("Fifth"));
            Assert.That(csvFile.Rows[1][2], Is.EqualTo("Sixth"));
            Assert.That(csvFile.Rows[2][0], Is.EqualTo("Seventh"));
            Assert.That(csvFile.Rows[2][1], Is.EqualTo("Eighth"));
            Assert.That(csvFile.Rows[2][2], Is.EqualTo("Ninth"));
        }
    }
}
